/**
 * Copyright (c) 2009 Cycle Information Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software and associated documentation files (the "Software"), to deal in the Software 
 * without restriction, including without limitation the rights to use, copy, modify, 
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 
 * permit persons to whom the Software is furnished to do so, subject to the following 
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies 
 * or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */
package com.cycleit.cycleframework.controller{
	import com.cycleit.cycleframework.entity.NavigationInfo;
	import com.cycleit.cycleframework.event.ViewStateChangeEvent;
	import com.cycleit.cycleframework.exception.CycleException;
	
	import flash.utils.Dictionary;
	
	import mx.binding.utils.ChangeWatcher;
	import mx.events.PropertyChangeEvent;
	/**
	 * Controlador dedicado a la gestión de los cambios de estados de vista, en función de los cambios 
	 * de una variable del modelo, a&#241;adiremos un watcher para escuchar los cambios y disparar las navegaciones
	 * pertinentes que nos posicionen la vista en un estado acorde con los datos del modelo.
	 */
	public class ViewController  {
     /**
      * Diccionario de mapeos entre watcher y navegaciones.
      */ 
      protected var watchers : Dictionary = new Dictionary();
	
	  public function ViewController() {
	  }
	  /**
	   * Añade un watcher a variables del modelo y asocia a los cambios
	   * en estas con la ejecución de metodos de refresco.
	   *  
	   * @param host Fuente de datos
	   * @param property Propiedad que cambia
	   * @param navigation Lista de estados de vista en el que la aplicacion debe quedar tras un cambio en <code>property</code>.
	   * 
	   * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la fuente de datos.
	   * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la propiedad.
	   * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la navegación deseada.
	   * @throws com.cycleit.cycleframework.exception.CycleException Cuando ya se ha añadido un watcher para esa navegación.
	   */ 		
	  public function addWatcher(host:Object, property:String, navigation:Array):void {
			if (host == null) { 
				throw new CycleException(CycleException.NEED_HOST);
			}
			    
			if (property == null) {
				throw new CycleException(CycleException.NEED_PROPERTY);
			}
			 
			if (navigation == null || navigation.length == 0) {
				throw new CycleException(CycleException.NEED_NAVIGATION);
			}
			 
			var id:String = host.toString()+property;
			 
			if(watchers[id] == null) {
				var cw:ChangeWatcher = ChangeWatcher.watch(host,property,genericNavigationHandler); 
				watchers[id] = new NavigationInfo(navigation,cw);
			}else{
				throw new CycleException( CycleException.NAVIGATION_ALREADY_EXIST, id );
			}
      }
	  
	  /**
	   * Añade un watcher a variables del modelo y asocia a los cambios
	   * en estas con un manejador.
	   *  
	   * @param host Fuente de datos
	   * @param property Propiedad que cambia
	   * @param handler manejador a medida para los cambios de <code>property</code>
	   * 
	   * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la fuente de datos.
	   * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la propiedad.
	   * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la navegación deseada.
	   * @throws com.cycleit.cycleframework.exception.CycleException Cuando ya se ha añadido un watcher para esa navegación.
	   */ 		
	  public function addHandler(host:Object, property:String, handler:Function):void {
			if (host == null) { 
				throw new CycleException(CycleException.NEED_HOST);
			}
			    
			if (property == null) {
				throw new CycleException(CycleException.NEED_PROPERTY);
			}
			 
			if (handler==null) {
				throw new CycleException(CycleException.NEED_HANDLER);
			}
			 
			var id:String = host.toString()+property;
			 
			if(watchers[id] == null) {
				var cw:ChangeWatcher = ChangeWatcher.watch(host,property,handler); 
				watchers[id] = new NavigationInfo(null,cw);
			}else{
				throw new CycleException( CycleException.NAVIGATION_ALREADY_EXIST, id );
			}
      }
	  
	  
	  /**
	   * Elimina un watcher asociado a una fuente de datos y una propiedad dentro de la misma.
	   *  
	   * @param host Fuente de datos
	   * @param property Propiedad inspeccionada
	   * 
	   */		
		public function removeWatcher( host:Object, property:String):void {
			var id:String = host.toString()+property;
			var nInfo:NavigationInfo = NavigationInfo(watchers[id]);
			if(nInfo){
				nInfo.watcher.unwatch();
				watchers[id] = undefined;
			 	delete watchers[id]; 
			}
		}
         
 
		/**
		 * De forma genérica si introducimos una navegación, cuando la variable inspeccionada 
		 * cambie, este metodo lanzara la navegacion de forma generica
		 * 
		 * @param event Evento despachado cuando cambia la propiedad deseada dentro de la fuente de datos.
		 * 
		 */
		protected function genericNavigationHandler(event:PropertyChangeEvent):void {
			var id:String = event.source.toString()+event.property;
			var nInfo:NavigationInfo = NavigationInfo(watchers[id]);
			if(nInfo){
				new ViewStateChangeEvent(ViewStateChangeEvent.MODIFY_VIEW,nInfo.navigation).dispatch();
			}    
		}
		  
		/**
		 * Devuelve un NavigationInfo en función del identificador de watcher que se haya introducido
		 *  
		 * @param id
		 * @return NavigationInfo
		 * 
		 */	
		protected function getWatcher( id : String ) : NavigationInfo {
			var watcher : NavigationInfo = watchers[ id ];
			 
			if ( watcher == null ){
				throw new CycleException( CycleException.VIEW_NOT_FOUND, id );
			}
			return watcher;
		}
	}   
}